#include "gplib.h"
#include <math.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>

extern const int g_nByteOffset[4];

/*
отрисовка изображений, пока только 8 бит
*/

/**
 * DrawBitmap - Вывод растровой картинки с поворотом на заданный угол и заменой цвета
 * первый пиксель изображения задает цвет прозрачности.
 * @param ctx - указатель на графический контекст
 * @param CenterX - x-координата центра изображения
 * @param CenterY - y-координата центра изображения
 * @param ImageWidth - ширина изображения
 * @param ImageHeight - высота изображения
 * @param Color - цвет, на который заменяются все цвета пикселей изображения, отличающихся от прозрачных
 * @param Bits - указатель на данные изображения (пока байт на пиксель)
 * @param CosAngle - косинус угла поворота
 * @param SinAngle - синус угла поворота
 * */
void DrawBitmap(TGraphicContext* ctx, int CenterX,int CenterY, int ImageWidth, int ImageHeight, char Color, const unsigned char *BitsPtr,float cosAngle,float sinAngle)
{
#define RMUL 1024 //множитель при переходе к целочисленной арифметике
   int sX,sY;
   char *ScrPtr;
   int ScreenWidth=ctx->nBytesPerLine;
   int minX=0,minY=0,maxX=0,maxY=0;
   int tX=0;
   int i=0,j=0;
   int icosAngle=RMUL*cosAngle;//целочисленное представление косинуса
   int isinAngle=RMUL*sinAngle;//целочисленное представление синуса
#define COMP  tX=(icosAngle*sX-isinAngle*sY)/RMUL-1;\
				if(tX>0)tX+=2;\
				if(tX<minX) minX=tX;\
				if(tX>maxX) maxX=tX;\
			  tX=(isinAngle*sX+icosAngle*sY)/RMUL-1;\
			  	  if(tX>0)tX+=2;\
			  	if(tX<minY) minY=tX;\
			  	if(tX>maxY) maxY=tX;

   sX=ImageWidth>>1;
   sY=ImageHeight>>1;
   COMP
   sX=ImageWidth>>1;
   sY=-ImageHeight>>1;
   COMP
   sX=-ImageWidth>>1;
   sY=ImageHeight>>1;
   COMP
   sX=-ImageWidth>>1;
   sY=-ImageHeight>>1;
   COMP
   //отсекаем те части изображения, которые не попадают на экран, чтобы не рисовать их
   if(minX+CenterX<0)
	   minX=-CenterX;
   if(minY+CenterY<0)
	   minY=-CenterY;
   if(maxX+CenterX>ctx->nScreenSizeX)
   	   maxX=ctx->nScreenSizeX-CenterX;
   if(maxY+CenterY>ctx->nScreenSizeY)
   	   maxY=ctx->nScreenSizeY-CenterY;

/*   MoveTo(ctx,minX+CenterX,minY+CenterY);
   LineTo(ctx,maxX+CenterX,minY+CenterY,50);

   MoveTo(ctx,maxX+CenterX,minY+CenterY);
   LineTo(ctx,maxX+CenterX,maxY+CenterY,50);

   MoveTo(ctx,maxX+CenterX,maxY+CenterY);
   LineTo(ctx,minX+CenterX,maxY+CenterY,50);

   MoveTo(ctx,minX+CenterX,maxY+CenterY);
   LineTo(ctx,minX+CenterX,minY+CenterY,50);*/
   ///////////////////////////////////////////////////////////////////////////////////
   /*
    * Неоптимизированная часть кода ниже выглядит так:
    * sX=cosAngle*j-(-sinAngle)*i+(ImageWidth>>1);
      sY=(-sinAngle)*j+cosAngle*i+(ImageHeight>>1);
      Это  по сути поворот точки на требуемый угол в противположную сторону.
      В целях оптимизации выносим из цикла лишние операции, вводим доп. переменные
      как
      	  sX=sX1+sX2;
   	  sY=sY1+sY2;

    */
   int sX1=minX*icosAngle;
   int sY1=minX*(-isinAngle);
   int sX2=isinAngle*minY+RMUL*(ImageWidth>>1);
   int sY2=icosAngle*minY+RMUL*(ImageHeight>>1);
   sX=sX1+sX2;
   sY=sY1+sY2;
   ScrPtr=ctx->pData+ ((int)minY+CenterY)*ctx->nBytesPerLine + (int)minX+CenterX;
   maxY-=minY;
   maxX-=minX;
   for(i=0;i<maxY;i++)
   {
	   sX1=minX*icosAngle;
	   sY1=minX*(-isinAngle);
	   sX=(sX1+sX2)>>10;//	/RMUL;
	   sY=(sY1+sY2)>>10;//	/RMUL;
	   for(j=0;j<maxX;j++)
	   {
		   sX=(sX1+sX2)>>10;//	/RMUL;
		   sY=(sY1+sY2)>>10;//	/RMUL;
		   sX1+=icosAngle;
		   sY1-=isinAngle;
		   if(sX>=0 && sX < ImageWidth && sY >= 0 && sY < ImageHeight)//проверка на попадание точки в исходное изображение
		   {
			   *(BYTE_REORDER(ScrPtr+j))=*(BitsPtr+sY*ImageWidth+sX);
		   }
	   }
	   sX2+=isinAngle;
	   sY2+=icosAngle;
   	   ScrPtr+=ScreenWidth;
   }
#undef RMUL
#undef SCALE
}




/**
 * StretchBitmap - Вывод растровой картинки с уменьшением её в 2 раза, поворотом и заменой цвета.
 * первый пиксель изображения задает цвет прозрачности.
 * @param ctx - указатель на графический контекст
 * @param CenterX - x-координата центра изображения
 * @param CenterY - y-координата центра изображения
 * @param ImageWidth - ширина изображения
 * @param ImageHeight - высота изображения
 * @param Color - цвет, на который заменяются все цвета пикселей изображения, отличающихся от прозрачных
 * @param Bits - указатель на данные изображения (пока байт на пиксель)
 * @param CosAngle - косинус угла поворота
 * @param SinAngle - синус угла поворота
 * */

void StretchBitmap(TGraphicContext* ctx, int CenterX,int CenterY,int ImageWidth,int ImageHeight,char Color, const char* BitsPtr,float CosAngle,float SinAngle)
{
#define RMUL 1024 //множитель при переходе к целочисленной арифметике
#define SCALE 2  // уменьшаем вдвое
   int sX,sY;
   char *ScrPtr;
   int ScreenWidth=ctx->nScreenSizeX;
   int minX=0,minY=0,maxX=0,maxY=0;
   int tX=0;
   int i=0,j=0;
   int icosAngle=RMUL*CosAngle;//целочисленное представление косинуса
   int isinAngle=RMUL*SinAngle;//целочисленное представление синуса
#define COMP  tX=(icosAngle*sX-isinAngle*sY)/RMUL-1;\
				if(tX>0)tX+=2;\
				if(tX<minX) minX=tX;\
				if(tX>maxX) maxX=tX;\
			  tX=(isinAngle*sX+icosAngle*sY)/RMUL-1;\
			  	  if(tX>0)tX+=2;\
			  	if(tX<minY) minY=tX;\
			  	if(tX>maxY) maxY=tX;

   sX=ImageWidth>>SCALE;
   sY=ImageHeight>>SCALE;
   COMP
   sX=ImageWidth>>SCALE;
   sY=-ImageHeight>>SCALE;
   COMP
   sX=-ImageWidth>>SCALE;
   sY=ImageHeight>>SCALE;
   COMP
   sX=-ImageWidth>>SCALE;
   sY=-ImageHeight>>SCALE;
   COMP
   //отсекаем те части изображения, которые не попадают на экран, чтобы не рисовать их
   if(minX+CenterX<0)
	   minX=-CenterX;
   if(minY+CenterY<0)
	   minY=-CenterY;
   if(maxX+CenterX>ctx->nScreenSizeX)
   	   maxX=ctx->nScreenSizeX-CenterX;
   if(maxY+CenterY>ctx->nScreenSizeY)
   	   maxY=ctx->nScreenSizeY-CenterY;
   ///////////////////////////////////////////////////////////////////////////////////
   /*
    * Неоптимизированная часть кода ниже выглядит так:
    * sX=cosAngle*j-(-sinAngle)*i+(ImageWidth>>1);
      sY=(-sinAngle)*j+cosAngle*i+(ImageHeight>>1);
      Это  по сути поворот точки на требуемый угол в противположную сторону.
      В целях оптимизации выносим из цикла лишние операции, вводим доп. переменные
      как
      	  sX=sX1+sX2;
   	  sY=sY1+sY2;

    */
   int sX1=minX*icosAngle;
   int sY1=minX*(-isinAngle);
   int sX2=isinAngle*minY;//+RMUL*(ImageWidth>>1);
   int sY2=icosAngle*minY;//+RMUL*(ImageHeight>>1);
   sX=sX1+sX2;
   sY=sY1+sY2;
   ScrPtr=ctx->pData + ((int)minY+CenterY)*ctx->nBytesPerLine + (int)minX+CenterX;
   maxY-=minY;
   maxX-=minX;
   for(i=0;i<maxY;i++)
   {
	   sX1=minX*icosAngle;
	   sY1=minX*(-isinAngle);
	   sX=(sX1+sX2)>>10;//	/RMUL;
	   sY=(sY1+sY2)>>10;//	/RMUL;
	   for(j=0;j<maxX;j++)
	   {
		   sX=(sX1+sX2)>>10;//(10-SCALE);//	/RMUL;
		   sY=(sY1+sY2)>>10;//(10-SCALE);//	/RMUL;
		   //обратное масштабирование, на самом деле учтено при расчете sX выше
		   sX<<=SCALE;
		   sY<<=SCALE;
		   if(sX>=-ImageWidth && sX < ImageWidth && sY >= -ImageHeight && sY < ImageHeight)//проверка на попадание точки в исходное изображение
		   {
		   	   *(BYTE_REORDER(ScrPtr+j))=*(BitsPtr+(((sY+ImageHeight)*ImageWidth+(sX+ImageWidth))>>1));
		   }
		   sX1+=icosAngle;
		   sY1-=isinAngle;
	   }
	   sX2+=isinAngle;
	   sY2+=icosAngle;
   	   ScrPtr+=ScreenWidth;
   }
#undef RMUL
#undef SCALE
}
/**
 * DrawIcon - вывод растрового изображения с выделением прозрачного цвета
 * @param ctx - указатель на графический контекст
 * @param x - X-координата верхнего левого угла изображения
 * @param y - Y-координата верхнего левого угла изображения
 * @param pIcon - указатель на структуру с параметрами изображения.
 * Первый пиксель изображения считается прозрачным.
 * */
void DrawIcon(TGraphicContext* ctx, int x, int y, struct TIcon* pIcon)
{
	int i=0,j=0;
	int StartScreenX=x;
	int StartScreenY=y;
	int ImageWidth=pIcon->nWidth;//Здесь храним ширину отображаемой картинки (актуально, если отображаем не всю картинку)
	int ImageHeight=pIcon->nHeight;//Здесь храним высоту отображаемой картинки (актуально, если отображаем не всю картинку)
	char* pImageData=pIcon->pData;
	char* pScreenData=ctx->pData;
	char TransparentColor=*(pIcon->pData);
	if(x >(int) ctx->nScreenSizeX || y > (int) ctx->nScreenSizeY || pIcon->nWidth < 0 || pIcon->nHeight < 0 || x+ImageWidth < 0 || y+ImageHeight < 0)
	{
		return;//верхняя левая точка не может быть отображена или изображение неверное
	}
	else
	{
		//Если всё изображение на экране не помещается, то рисуем то, что есть.
		if(x+ImageWidth > ctx->nScreenSizeX)
			ImageWidth= ctx->nScreenSizeX - x;
		if(y + ImageHeight > ctx->nScreenSizeY)
			ImageHeight= ctx->nScreenSizeY-y;
		//Если левая верхняя вершина за пределами экрана, то часть картинки попадает, всё равно рисуем, но корректируем область отрисовки.
		if(x < 0)
		{
			StartScreenX=0;//начинаем рисовать с 0
			pImageData+=ImageWidth+x;
			ImageWidth=pIcon->nWidth+x;
		}
		if(y < 0)
		{
			StartScreenY=0;
			pImageData+=(-y)*pIcon->nWidth;
			ImageHeight=pIcon->nHeight+y;
		}
	}
	pScreenData=ctx->pData+StartScreenY*ctx->nBytesPerLine+StartScreenX;
	for(i=0;i<ImageHeight;i++)
	{
		for(j=0;j<ImageWidth;j++)
		{
			if(*(pImageData+j)!=TransparentColor)
				*(BYTE_REORDER(pScreenData+j))=*(pImageData+j);
		}
		pScreenData+=ctx->nBytesPerLine;
		pImageData+=pIcon->nWidth;
	}
}

/**
 * DrawIconMono - вывод растрового изображения с выделением прозрачного цвета и заменой всех непрозрачных цветов на один
 * @param ctx - указатель на графический контекст.
 * @param x - X-координата верхнего левого угла изображения.
 * @param y - Y-координата верхнего левого угла изображения.
 * @param pIcon - указатель на структуру с параметрами изображения.
 * @param Color - цвет, на который заменяются цвета всех непрозрачных пикселей.
 * Первый пиксель изображения считается прозрачным.
 * */
void DrawIconMono(TGraphicContext* ctx, int x, int y, struct TIcon* pIcon, char Color)
{
	int i=0,j=0;
	int StartScreenX=x;
	int StartScreenY=y;
	int ImageWidth=pIcon->nWidth;//Здесь храним ширину отображаемой картинки (актуально, если отображаем не всю картинку)
	int ImageHeight=pIcon->nHeight;//Здесь храним высоту отображаемой картинки (актуально, если отображаем не всю картинку)
	char* pImageData=pIcon->pData;
	char* pScreenData=ctx->pData;
	char TransparentColor=*(pIcon->pData);
	if(x >(int) ctx->nScreenSizeX || y > (int) ctx->nScreenSizeY || pIcon->nWidth < 0 || pIcon->nHeight < 0 || x+ImageWidth < 0 || y+ImageHeight < 0)
	{
		return;//верхняя левая точка не может быть отображена или изображение неверное
	}
	else
	{
		//Если всё изображение на экране не помещается, то рисуем то, что есть.
		if(x+ImageWidth > ctx->nScreenSizeX)
			ImageWidth= ctx->nScreenSizeX - x;
		if(y + ImageHeight > ctx->nScreenSizeY)
			ImageHeight= ctx->nScreenSizeY-y;
		//Если левая верхняя вершина за пределами экрана, но часть картинки попадает, всё равно рисуем, но корректируем область отрисовки.
		if(x < 0)
		{
			StartScreenX=0;//начинаем рисовать с 0
			pImageData+=ImageWidth+x;
			ImageWidth=pIcon->nWidth+x;
		}
		if(y < 0)
		{
			StartScreenY=0;
			pImageData+=(-y)*pIcon->nWidth;
			ImageHeight=pIcon->nHeight+y;
		}
	}
	pScreenData=ctx->pData+StartScreenY*ctx->nBytesPerLine+StartScreenX;
	for(i=0;i<ImageHeight;i++)
	{
		for(j=0;j<ImageWidth;j++)
		{
			if(*(pImageData+j)!=TransparentColor)
				*(BYTE_REORDER(pScreenData+j))=Color;
		}
		pScreenData+=ctx->nBytesPerLine;
		pImageData+=pIcon->nWidth;
	}
}


/**
 * DrawIconCut - вывод растрового изображения с выделением прозрачного цвета и уменьшением изображения вдвое
 * @param ctx - указатель на графический контекст
 * @param x - X-координата верхнего левого угла изображения
 * @param y - Y-координата верхнего левого угла изображения
 * @param pIcon - указатель на структуру с параметрами изображения.
 * Первый пиксель изображения считается прозрачным.
 * */
void DrawCutIcon(TGraphicContext* ctx, int x, int y, struct TIcon* pIcon)
{
	#define SHIFTVAL 1 //Масштабируем сдвигами, задаем значение сдвига
	int i=0,j=0;
	int StartScreenX=x;
	int StartScreenY=y;
	int ImageWidth=pIcon->nWidth>>SHIFTVAL;//Здесь храним ширину отображаемой картинки (актуально, если отображаем не всю картинку)
	int ImageHeight=pIcon->nHeight>>SHIFTVAL;//Здесь храним высоту отображаемой картинки (актуально, если отображаем не всю картинку)
	char* pImageData=pIcon->pData;
	char* pScreenData=ctx->pData;
	char TransparentColor=*(pIcon->pData);
	if(x >(int) ctx->nScreenSizeX || y > (int) ctx->nScreenSizeY || pIcon->nWidth < 0 || pIcon->nHeight < 0 || x+ImageWidth < 0 || y+ImageHeight < 0)
	{
		return;//верхняя левая точка не может быть отображена или изображение неверное
	}
	else
	{
		//Если всё изображение на экране не помещается, то рисуем то, что есть.
		if(x+ImageWidth > ctx->nScreenSizeX)
			ImageWidth= ctx->nScreenSizeX - x;
		if(y + ImageHeight > ctx->nScreenSizeY)
			ImageHeight= ctx->nScreenSizeY-y;
		//Если левая верхняя вершина за пределами экрана, но часть картинки попадает, всё равно рисуем, но корректируем область отрисовки.
		if(x < 0)
		{
			StartScreenX=0;//начинаем рисовать с 0
			pImageData+=ImageWidth+x;
			ImageWidth=pIcon->nWidth+x;
		}
		if(y < 0)
		{
			StartScreenY=0;
			pImageData+=(-y)*pIcon->nWidth;
			ImageHeight=pIcon->nHeight+y;
		}
	}
	pScreenData=ctx->pData+StartScreenY*ctx->nBytesPerLine+StartScreenX;
	for(i=0;i<ImageHeight;i++)
	{
		for(j=0;j<ImageWidth;j++)
		{
			if(*(pImageData+(j<<SHIFTVAL))!=TransparentColor)
				*(BYTE_REORDER(pScreenData+j))=*(pImageData+(j<<SHIFTVAL));
		}
		pScreenData+=ctx->nBytesPerLine;
		pImageData+=(pIcon->nWidth)<<SHIFTVAL;
	}
}


/**
 * DrawPixmap - вывод растровой картинки.
 * @param ctx - указатель на графический контекст
 * @param x - X-координата верхнего левого угла изображения
 * @param y - Y-координата верхнего левого угла изображения
 * @param pIcon - указатель на структуру с параметрами изображения.
 * */
void DrawPixmap(TGraphicContext* ctx, int x, int y, struct TIcon* pIcon)
{
	int i=0,j=0;
	int StartScreenX=x, StartScreenY=y;//экранные координаты верхнего левого угла изображения (изменяются, если выводим фрагмент изображения)
	int ScreenWidth=ctx->nBytesPerLine;//Ширина экрана в байтах
	int ImageWidth=pIcon->nWidth;//Здесь храним ширину отображаемой картинки (актуально, если отображаем не всю картинку)
	int ImageHeight=pIcon->nHeight;//Здесь храним высоту отображаемой картинки (актуально, если отображаем не всю картинку)
	char* pImageData=pIcon->pData;
	char* pScreenData=ctx->pData;
	char TransparentColor=*(pIcon->pData);
	if(x >(int) ctx->nScreenSizeX || y > (int) ctx->nScreenSizeY || pIcon->nWidth < 0 || pIcon->nHeight < 0 || x+ImageWidth < 0 || y+ImageHeight < 0)
		return;//верхняя левая точка не может быть отображена или изображение неверное
	else
	{
		//Если всё изображение на экране не помещается, то рисуем то, что есть.
		if(x+ImageWidth > ctx->nScreenSizeX)
			ImageWidth= ctx->nScreenSizeX - x;
		if(y + ImageHeight > ctx->nScreenSizeY)
			ImageHeight= ctx->nScreenSizeY-y;
		//Если левая верхняя вершина за пределами экрана, но часть картинки попадает, всё равно рисуем, но корректируем область отрисовки.
		if(x < 0)
		{
			StartScreenX=0;//начинаем рисовать с 0
			pImageData+=ImageWidth+x;
			ImageWidth=pIcon->nWidth+x;
		}
		if(y < 0)
		{
			StartScreenY=0;
			pImageData+=(-y)*pIcon->nWidth;
			ImageHeight=pIcon->nHeight+y;
		}
	}
	pScreenData=ctx->pData+StartScreenY*ctx->nBytesPerLine+StartScreenX;
	for(i=0;i<ImageHeight;i++)
	{
		for(j=0;j<ImageWidth;j++)
		{
			*(BYTE_REORDER(pScreenData+j))=*(pImageData+j);
		}
		pScreenData+=ScreenWidth;
		pImageData+=ImageWidth;
	}
}

